home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac-Source 1994 July
/
Mac-Source_July_1994.iso
/
Misc Utils
/
Vestal Version
/
Source Code
/
VV.c
< prev
Wrap
Text File
|
1993-08-02
|
12KB
|
560 lines
//
// VV.c
//
// Written by Alastair Rankine
// Copyright © 1993 All Rights Reserved.
// And All That Sort of Stuff.
//
// Version History:
// 1.0 - First Development
//
// 1.1 - Upgraded to DragonSmith 1.1
// - Made ProcessDoc _far_ more intelligent. Is alias aware, can recognise
// existing version numbers (even hidden amongst whitespace), and is more
// bulletproof in general.
// - Added preferences. Can now specify whether or not to quit after
// processing, whether or not to ignore aliases, and whether or not
// to try to replace existing version numbers.
// - Status dialog box. Non-modal and quite hoopy all round really.
//
#include <string.h>
#include <ctype.h>
#include "Dragon.h"
#include "EventUtils.h"
// System 7.0 only Dialog Manager routines. These are out of technote something or other,
// and not included in the standard THINK C header files.
pascal OSErr GetStdFilterProc(ModalFilterProcPtr *theProc)
= { 0x303C, 0x0203, 0xAA68 }; // Returns a pointer to the Dialog Manager's standard dialog filter
pascal OSErr SetDialogDefaultItem(DialogPtr theDialog, short newItem)
= { 0x303C, 0x0304, 0xAA68 }; // Indicates to the dialog manager which item is default. Will then alias the return
// & enter keys to this item, and also bold border it for you (yaaaaa!)
pascal OSErr SetDialogCancelItem(DialogPtr theDialog, short newItem)
= { 0x303C, 0x0305, 0xAA68 }; // Indicates which item should be aliased to escape or Command - .
pascal OSErr SetDialogTracksCursor(DialogPtr theDialog, Boolean tracks)
= { 0x303C, 0x0306, 0xAA68 }; // Tells the dialog manager that there is an edit line in this dialog, and
// it should track and change to an I-Beam cursor when over the edit line
// Pascal string compare function used by ProcessFile():
int Pstrcmp(const unsigned char *s1, const unsigned char *s2);
// Used in the Preferences Dialog:
pascal void lineProc(WindowPtr w, short item);
// Is this string a version number?
char IsVersionStr(char *s);
// Item numbers for the preferences dialog:
enum {
iOK = 1,
iCancel,
iQuitAfter,
iReplaceExistVNum,
iIgnoreAlias,
iTitle,
iLine
};
// Item numbers for status dialog:
enum {
iStop = 1,
iProcTxt,
iCurFile
};
// Our preferences:
enum {
prefVersPrefs = prefDragonPrefs + 1
};
// Menu item number for the Preferences… dialog:
const int itemPrefs = 8;
// Our dialog IDs:
enum {
dlogPref = 128,
dlogStatus
};
// Our preferences:
struct VPrefs {
char quitAfter;
char replaceExistVNum;
char ignoreAliases;
};
//
// The class definition itself!
//
class DVers: public Dragon {
protected:
struct VPrefs **VersPrefs; // The prefs stored in the prefs file
struct VPrefs p; // Prefs to use at run-time.
DialogPtr stat; // The status dialog, if any.
Handle curFile; // Handle to the static text item for the
// current file name.
public:
DVers(void);
void ProcessFile(void);
protected:
void DoAbout(void);
void AdjustMenusBusy(void);
void AdjustMenusIdle(void);
void DoMenu(long menuItemCode);
void DoMouseDown(EventRecord *theEvent);
void DoDrag(WindowPtr wind, Point p);
void DoPrefsDialog(void);
void ReadPrefs(void);
void BeginProcessing(void);
void DoEvent(EventRecord *er);
void EndProcessing(void);
};
Dragon *CreateGDragon (void)
{
return (Dragon *) new DVers;
}
DVers::DVers (void)
{
VersPrefs = NULL;
p.quitAfter = 0;
p.replaceExistVNum = 1;
p.ignoreAliases = 0;
dirDepthLimit = -10;
autoQuit = FALSE;
}
void
DVers::DoAbout(void)
{
Alert(128, nil);
}
void
DVers::ProcessFile (void)
{
short rf = 0, i;
Handle vers;
FSSpec copy;
OSErr err;
// Display the filename:
if(curFile)
SetIText(curFile, curDocFSS->name);
if(!p.ignoreAliases) {
Boolean fold, alias;
// Make a copy of the FSSpec, ready to hold the FSSpec of the resolved alias file.
BlockMove(curDocFSS, ©, sizeof(FSSpec));
err = ResolveAliasFile(©, 1, &fold, &alias);
// If it's a folder, or the alias can't be resolved, exit quietly...
if(fold || alias && (err == nsvErr || err == fnfErr || err == dirNFErr))
goto yo_later_dude;
else if(err) {
Error(err);
goto yo_later_dude;
}
rf = FSpOpenResFile(©, fsRdPerm);
} else
rf = FSpOpenResFile(curDocFSS, fsRdPerm);
if(rf == -1) {
err = ResError();
// File may either have no resource fork or an empty one, in which case we
// exit quietly:
if(err && err != resFNotFound && err != eofErr)
Error(err);
rf = 0;
goto yo_later_dude;
}
if(vers = Get1Resource('vers', 1)) {
short namelen = curDocFSS->name[0], verslen = (*vers)[6];
Str255 n, v; // New name and version number, respectively.
char *lastWd, *versNo = (char *)v;
// Copy the version string
BlockMove(*vers + 7, v, verslen);
v[verslen] = 0;
// Don't need the resource file any more...
CloseResFile(rf);
err = ResError();
if(err) {
Error(err);
goto yo_later_dude;
}
// Copy the old name into n + 1 (leaving room for the length byte)
BlockMove(curDocFSS->name + 1, n + 1, namelen);
n[namelen + 1] = 0;
// Remove trailing space on the name:
lastWd = (char *)n + namelen;
while(isspace(*lastWd) && (lastWd > (char *)n))
*lastWd-- = 0;
// First check the version string: some applications (eg Speedometer)
// store the application name in here as well, believe it or not. What
// we do to get around this is to iterate through the string, word
// by word until a valid version string is found. If one can't be found,
// we abort.
do {
// Have we found a version string
if(IsVersionStr(versNo))
break;
// Go to the next word:
if(versNo = strchr(versNo, ' '))
versNo++;
} while(versNo);
if(!versNo)
goto yo_later_dude;
else {
// Null-terminate the version string:
char *c = versNo;
do
c++;
while(*c && !isspace(*c));
*c = 0;
}
// Get the last word of the filename:
lastWd = strrchr((char *)n + 1, ' ');
// If there's more than one word, we need to investigate further...
if(lastWd) {
// Reset lastWd if there was no version number:
if(!IsVersionStr(lastWd + 1))
lastWd = NULL;
// Or abort if we're not to replace the one that's there:
else if(!p.replaceExistVNum)
goto yo_later_dude;
}
// No version number? Well set lastWd to point to where one should be!
if(!lastWd)
lastWd = (char *)n + namelen;
// Remove any existing trailing spaces:
while(isspace(*lastWd) && (lastWd > (char *)n))
lastWd--;
// Add a space before the new version string:
*++lastWd = ' ';
// Append the version string.
strcpy(lastWd + 1, versNo);
// Convert to a pascal string and check the length.
n[0] = strlen((char *)n + 1);
if(n[0] >= 63)
goto yo_later_dude;
// Do the rename if the filename has changed:
if(Pstrcmp(curDocFSS->name, n)) {
err = FSpRename(curDocFSS, n);
if(err) {
Error(err);
goto yo_later_dude;
}
// Now tell the finder about it:
BlockMove(curDocFSS, ©, sizeof(FSSpec));
CopyPStr(n, copy.name);
FSpRefreshFinderDisplay(©);
}
} else {
CloseResFile(rf);
err = ResError();
if(err)
Error(err);
}
yo_later_dude:
if(rf)
CloseResFile(rf);
}
void
DVers::BeginProcessing(void)
{
Rect r;
inherited::BeginProcessing();
stat = GetNewDialog(dlogStatus, nil, (WindowPtr)-1L);
if(!stat)
return;
// Set the GrafPort
SetPort(stat);
// Set the default buttons
SetDialogCancelItem(stat, 1);
// Set the curFile variable;
GetDItem(stat, iCurFile, &r, &curFile, &r);
}
void
DVers::DoEvent(EventRecord *er)
//
// Handle our modeless status dialog if neccessary.
//
{
if(IsDialogEvent(er)) {
int item = 0;
DialogPtr dlog = FrontWindow();
if(er->what == keyDown) {
// Check for Esc or cmd-. keypresses.
if(IsCancelEvent(er))
item = iStop;
// Get any? (ooer!)
if(item) {
ControlHandle ch;
Rect r;
// Toggle the control on and off again to give some feedback:
GetDItem(dlog, item, &r, &ch, &r);
HiliteControl(ch, 1);
Delay(4, &r);
HiliteControl(ch, 0);
}
}
// Let the dialog manager do whatever's neccessary:
DialogSelect(er, &dlog, &item);
// Have we aborted?
if(dlog == stat && item == iStop)
StopProcessing(noErr);
} else
inherited::DoEvent(er);
}
void
DVers::DoMouseDown(EventRecord *theEvent)
{
WindowPtr whichWindow;
short domain;
domain = FindWindow(theEvent->where, &whichWindow);
switch (domain) {
case inDrag:
DoDrag(whichWindow, theEvent->where);
break;
default:
inherited::DoMouseDown(theEvent);
break;
}
}
void
DVers::DoDrag(WindowPtr wind, Point mPoint)
{
if(wind != FrontWindow())
SelectWindow(wind);
else
DragWindow(wind, mPoint, &screenBits.bounds);
}
void
DVers::EndProcessing(void)
{
inherited::EndProcessing();
// Set this variable if our prefs tell us to quit!
autoQuit = p.quitAfter;
// Get rid of the status box if neccessary:
if(stat) {
DisposDialog(stat);
stat = NULL;
curFile = NULL;
}
}
void
DVers::DoMenu (long menuItemCode)
{
short menuID, itemNum;
menuID = menuItemCode >> 16;
itemNum = menuItemCode & 0xFFFF;
// Our preferences… menu item needs to be checked for...
if (menuID == mEdit && itemNum == itemPrefs)
DoPrefsDialog();
else
inherited::DoMenu (menuItemCode);
}
void
DVers::AdjustMenusBusy (void)
{
inherited::AdjustMenusBusy ();
DisableItem (editMenu, itemPrefs); // Disable the Preferences… item
}
void
DVers::AdjustMenusIdle (void)
{
inherited::AdjustMenusIdle ();
EnableItem (editMenu, itemPrefs);
}
void
DVers::DoPrefsDialog(void)
//
// Puts a modal (urk! I know!) dialog box up to edit the preferences.
//
{
DialogPtr dlog;
Rect r;
ControlHandle CtlQuitAfter, CtlIgnoreAlias, CtlReplaceExistVNum;
int item;
dlog = GetNewDialog(dlogPref, nil, (WindowPtr)-1L);
if(!dlog)
return;
SetPort(dlog);
// Set the default buttons
SetDialogDefaultItem(dlog, iOK);
SetDialogCancelItem(dlog, iCancel);
// Set the lineProc item:
GetDItem(dlog, iLine, &item, &CtlQuitAfter, &r);
SetDItem(dlog, iLine, item, lineProc, &r);
// Get the control handles:
GetDItem(dlog, iQuitAfter, &item, &CtlQuitAfter, &r);
GetDItem(dlog, iIgnoreAlias, &item, &CtlIgnoreAlias, &r);
GetDItem(dlog, iReplaceExistVNum, &item, &CtlReplaceExistVNum, &r);
SetCtlValue(CtlQuitAfter, p.quitAfter);
SetCtlValue(CtlIgnoreAlias, p.ignoreAliases);
SetCtlValue(CtlReplaceExistVNum, p.replaceExistVNum);
do {
ControlHandle ch = NULL;
ModalDialog(nil, &item);
switch (item) {
case iQuitAfter:
ch = CtlQuitAfter;
break;
case iIgnoreAlias:
ch = CtlIgnoreAlias;
break;
case iReplaceExistVNum:
ch = CtlReplaceExistVNum;
break;
default:
break;
}
// Toggle the control:
if(ch)
SetCtlValue(ch, !GetCtlValue(ch));
} while (item != iOK && item != iCancel);
if (item == OK) {
p.quitAfter = GetCtlValue(CtlQuitAfter);
p.ignoreAliases = GetCtlValue(CtlIgnoreAlias);
p.replaceExistVNum = GetCtlValue(CtlReplaceExistVNum);
// Save the preferences:
BlockMove(&p, *VersPrefs, sizeof(struct VPrefs));
preferences->SavePrefResource(prefVersPrefs);
}
DisposDialog(dlog);
}
void DVers::ReadPrefs (void)
{
inherited::ReadPrefs ();
VersPrefs = (struct VPrefs **) preferences->GetPrefResource (prefVersPrefs);
if (VersPrefs)
BlockMove(*VersPrefs, &p, sizeof(struct VPrefs));
}
pascal void
lineProc(WindowPtr w, short item)
//
// Does a double line in the relevant user item...
//
{
Handle h;
Rect r;
GetDItem(w, item, &h, &h, &r);
MoveTo(r.left, r.top);
LineTo(r.right, r.top);
MoveTo(r.left, r.bottom - 1);
LineTo(r.right, r.bottom - 1);
}
int
Pstrcmp(const unsigned char *s1, const unsigned char *s2)
//
// like strcmp, only for pascal strings (!)
//
{
int i = *s1;
if (i == *s2)
for (; i; i--)
if (s1[i] != s2[i])
return(i);
return(i);
}
char
IsVersionStr(char *c)
//
// Returns true if the word (ie delimited by null or whitespace) pointed to by wd
// is a valid version number.
//
{
char alphCount = 2;
for(; *c && !isspace(*c) && alphCount; c++) {
// Not a version number if it has more than one alphabetic character...
if(isalpha(*c))
alphCount--;
// Version numbers can only have digits or dots...
else if(!isdigit(*c) && *c != '.')
alphCount = 0;
}
return(alphCount != 0);
}